#pragma once
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/gccmacro.h>
#include <stdlib.h>
#include "end_of_seq.hpp"

enum { SAMPLE_RATE = 44100 };

template <typename Generator>
class SoundOut
{
public:
    SoundOut();
    bool tick();
    ~SoundOut();
private:
    enum { BUF_SIZE = 2 * 1024 };
    Generator in_;
    pa_simple *pa_;
    short *buf_;
    int pos_;
};


namespace
{
    const pa_sample_spec ss = {
        PA_SAMPLE_S16LE, // format
        SAMPLE_RATE, // rate
        1 // channels
    };
    int error;
}

template <typename Generator>
SoundOut<Generator>::SoundOut():
    pa_(pa_simple_new(NULL, "sound_engine", PA_STREAM_PLAYBACK, 
                      NULL, "playback", &ss, NULL, NULL, &error)),
    buf_(new short[BUF_SIZE]),
    pos_(0)
{   
    if (!pa_) 
        abort();
}

template <typename Generator>
bool SoundOut<Generator>::tick()
{
    try
    {
        const float in = in_();
        buf_[pos_++] = in > 1 ? 0x7FFF : in < -1 ? -0x7FFF : in * 0x7FFF;
        if (pos_ >= BUF_SIZE)
        {
            int error;
            if (pa_simple_write(pa_, buf_, BUF_SIZE * sizeof(*buf_), &error) < 0) 
                abort();
            pos_ = 0;
        }
        return true;
    }
    catch (EndOfSeq &e)
    {
        return false;
    }
}

template <typename Generator>
SoundOut<Generator>::~SoundOut()
{
    delete [] buf_;
    int error;
    pa_simple_drain(pa_, &error);
    pa_simple_free(pa_);
}
